package com.checx;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;

public class ThroughputTest {
    // 计算阶乘
    public static long factorial(long n) {
        long tmp = n;
        for (long i = n - 1; i > 1; i--) {
            tmp = tmp * i;
        }
        return tmp;
    }

    // 计算随机向量的内积
    public static double randomScalarProduct(long size) {
        double[] arr1 = new Random().doubles(size).toArray();
        double[] arr2 = new Random().doubles(size).toArray();
        double sum = 0;
        for (int i = 0; i < arr1.length; i++) {
            sum += arr1[i] * arr2[i];
        }
        return sum;
    }

    public static void test(Function<Long, ?> fun, long num, long size, int threads) {
        long startTime = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(threads);
        final ThreadLocal<Long> max = ThreadLocal.withInitial(() -> 0L);
        final ThreadLocal<Long> min = ThreadLocal.withInitial(() -> Long.MAX_VALUE);
        CountDownLatch countDownLatch = new CountDownLatch((int) num);
        for (int i = 0; i < num; i++) {
            executorService.execute(() -> {
                long startTime1 = System.currentTimeMillis();
                fun.apply(size);
                long duration1 = System.currentTimeMillis() - startTime1;
                if (duration1 > max.get()) max.set(duration1);
                if (duration1 < min.get()) min.set(duration1);
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        Queue<Long> maxs = new LinkedBlockingDeque<>();
        Queue<Long> mins = new LinkedBlockingDeque<>();
        CountDownLatch countDownLatch1 = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            executorService.execute(() -> {
                if(max.get() != null) maxs.add(max.get());
                if(min.get() != null) mins.add(min.get());
                countDownLatch1.countDown();
                try {
                    countDownLatch1.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }
        try {
            countDownLatch1.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        executorService.shutdown();
        Long maxVal = maxs.stream().max(Long::compareTo).get();
        Long minVal = mins.stream().min(Long::compareTo).get();

        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        long throughput = num * 1000 / duration;
        System.out.println("吞吐量：" + throughput + " ops/s");
        System.out.println("最长：" + maxVal + "ms" + " 最短：" + minVal + "ms");
    }

    public static void main(String[] args) {
        test(ThroughputTest::randomScalarProduct, 100000, 50<<10, 4);
    }
}
